import {GitTools} from './git-command-helper'
import * as step from '@flow-step/step-toolkit'
import * as fs from 'fs'
import * as util from './util'
import {IGitSourceParams} from './git-source-params'

export async function calculateCommitRange(input: IGitSourceParams) {
    if (input.previousCommitId === undefined) {
        return
    }
    if (input.branch === undefined) {
        return
    }
    let git = new GitTools()
    let lastCommitId = (await git.getCommitId(input.projectDir, 'HEAD')).stdout
    step.info('Calculating increasement commit scope: ')
    step.info(
        `${input.gitRepo}.${input.branch}.preCommitId=${input.previousCommitId}`
    )
    fs.writeFileSync(
        `${input.workDir}${util.SEP}commitInfo`,
        `${input.gitRepo}.${input.branch}.preCommitId=${input.previousCommitId}${util.EOL}`,
        {flag: 'a'}
    )
    step.info(`${input.gitRepo}.${input.branch}.lastCommitId=${lastCommitId}`)
    fs.appendFileSync(
        `${input.workDir}${util.SEP}commitInfo`,
        `${input.gitRepo}.${input.branch}.lastCommitId=${lastCommitId}${util.EOL}`
    )
}

export async function recordInfo(input: IGitSourceParams, order: number) {
    //计算commitId
    step.info(`RECORD REPOSITORY INFO ${input.projectDir}`)
    let git = new GitTools()
    let format: string = `###commitId###:%H###commitTime###:%cd###commitAuthor###:%an <%ae>###commitMsg###:%s`
    if (input.commitId != undefined && input.commitId != 'null') {
        step.info(
            `Git commit version of current build is locked to ${input.commitId}`
        )
    }
    if (input.branchMode === true) {
        step.info('The pipeline code source opens the branch mode')
        let output = (await git.log(input.projectDir, 1, format)).stdout.join(util.EOL)
        fs.writeFileSync(
            `${input.projectDir}${util.SEP}code_commit.log`,
            output,
            {
                flag: 'a'
            })
    } else if (
        input.previousCommitId != undefined &&
        input.previousCommitId != 'null'
    ) {
        step.info(`Get the commitId ${input.previousCommitId} for the last run`)
        let currentCommitId = (await git.getCommitId(input.projectDir, 'HEAD'))
            .stdout
        if (input.depth != 0) {
            step.info(
                'The pipeline code source opens the clone depth ,the change record may not match the actual situation'
            )
            let output = (await git.log(input.projectDir, 1, format)).stdout.join(util.EOL)
            step.info(`${output}`)
            fs.writeFileSync(
                `${input.projectDir}${util.SEP}code_commit.log`,
                output,
                {
                    flag: 'a'
                })
        } else if (currentCommitId === input.previousCommitId) {
            step.info(
                'The last commit of the pipeline is the same as the current commit'
            )
            let output = (await git.log(input.projectDir, 1, format)).stdout.join(util.EOL)
            step.info(`${output}`)
            fs.writeFileSync(
                `${input.projectDir}${util.SEP}code_commit.log`,
                output,
                {
                    flag: 'a'
                })
        } else {
            step.info(
                'There are changes between the last commit of the pipeline and this commit'
            )
            let output = (
                await git.log(
                    input.projectDir,
                    20,
                    format,
                    input.previousCommitId,
                    currentCommitId
                )
            ).stdout.join(util.EOL)
            step.info(`${output}`)
            fs.writeFileSync(
                `${input.projectDir}${util.SEP}code_commit.log`,
                output,
                {
                    flag: 'a'
                })
        }
    } else {
        step.info('The last commitId was not fetched')
        let output = (await git.log(input.projectDir, 20, format)).stdout.join(util.EOL)
        step.info(`${output}`)
        fs.writeFileSync(
            `${input.projectDir}${util.SEP}code_commit.log`,
            output,
            {
                flag: 'a'
            })
    }
    if (fs.existsSync(`${input.projectDir}${util.SEP}code_commit.log`)) {
        let contents = fs.readFileSync(
            `${input.projectDir}${util.SEP}code_commit.log`,
            'utf-8'
        )
        if (contents.trim()) {
            step.info(`Get git log success`)
        } else {
            step.error(`Get git log failed, require the most recent commit info.`)
            let output = (await git.log(input.projectDir, 1, format)).stdout.join(util.EOL)
            step.info(`${output}`)
            fs.writeFileSync(
                `${input.projectDir}${util.SEP}code_commit.log`,
                output,
                {
                    flag: 'a'
                })
        }
    }
    //commit信息收集
    if (input.branch === undefined) {
        return
    }
    let key = `${input.gitRepo}.${input.branch}.commitMsg`
    let path = `${input.workDir}${util.SEP}params`
    let contextPath = `${input.workDir}${util.SEP}stepContext`
    const commitList = new Array()
    let contents = fs.readFileSync(
        `${input.projectDir}${util.SEP}code_commit.log`,
        'utf-8'
    )
    if (!contents.trim()) {
        step.error('The code_commit.log is empty, failed to record commit info !!!')
        return
    }
    let lines = contents.split(util.EOL)
    lines.forEach(line => {
        commitList.push(buildCommit(line).replace(/'/g, ''))
    })

    step.info(commitList[0])
    let lastCommitMsg: string = decodeURIComponent(JSON.parse(commitList[0]).commitMsg)
    step.addOutput(`${key}=[${commitList.join()}]`);
    let sourceName = extractRepoName(input.repo)
    writeCodeInfoToParams(
        'outputsToEnv.',
        '',
        commitList,
        input.branch,
        lastCommitMsg,
        sourceName,
        input.type,
        input.previousCommitId
    )
    writeCodeInfoToParams(
        'outputsToEnv.',
        '_' + order,
        commitList,
        input.branch,
        lastCommitMsg,
        sourceName,
        input.type,
        input.previousCommitId
    )
    //write step context
    writeCodeInfo(
        contextPath,
        '',
        '',
        commitList,
        input.branch,
        lastCommitMsg,
        sourceName,
        input.type,
        input.previousCommitId
    )
    writeCodeInfo(
        contextPath,
        '',
        '_' + order,
        commitList,
        input.branch,
        lastCommitMsg,
        sourceName,
        input.type,
        input.previousCommitId
    )
    //write .env
    writeCodeInfoToEnv(
        '',
        commitList,
        input.branch,
        lastCommitMsg,
        sourceName,
        input.type,
        input.previousCommitId
    )
    writeCodeInfoToEnv(
        '_' + order,
        commitList,
        input.branch,
        lastCommitMsg,
        sourceName,
        input.type,
        input.previousCommitId
    )

    fs.unlinkSync(`${input.projectDir}${util.SEP}code_commit.log`)
    step.info('Complete collecting commit info')
}

function writeCodeInfoToEnv(
    suffix: string,
    commitList: string[],
    branch: string,
    lastCommitMsg: string,
    sourceName: string,
    repoType: string,
    previousCommitId?: string
) {
    
    step.exportEnvVar('CI_COMMIT_SHA' + suffix, JSON.parse(commitList[0]).commitId);
    step.exportEnvVar('CI_COMMIT_ID' + suffix, JSON.parse(commitList[0]).commitId.substring(0, 8));
    step.exportEnvVar('CI_COMMIT_REF_NAME' + suffix, branch);
    step.exportEnvVar('CI_COMMIT_TITLE' + suffix, lastCommitMsg);
    step.exportEnvVar('CI_SOURCE_NAME' + suffix, sourceName);
    step.exportEnvVar('CI_SOURCE_TYPE' + suffix, repoType);
    if(previousCommitId) {
        step.exportEnvVar('CI_COMMIT_BEFORE_SHA' + suffix, previousCommitId);
    }
    if(commitList.length > 0 && JSON.parse(commitList[0]).commitAuthor) {
        step.exportEnvVar('CI_COMMIT_AUTHOR' + suffix, decodeURIComponent(JSON.parse(commitList[0]).commitAuthor));
    }

}

function writeCodeInfo(
    path: string,
    prefix: string,
    suffix: string,
    commitList: string[],
    branch: string,
    lastCommitMsg: string,
    sourceName: string,
    repoType: string,
    previousCommitId?: string
) {
    fs.writeFileSync(path, prefix + 'CI_COMMIT_SHA' + suffix + '=' + JSON.parse(commitList[0]).commitId + util.EOL, {flag: 'a'})
    fs.appendFileSync(path, prefix + 'CI_COMMIT_ID' + suffix + '=' + JSON.parse(commitList[0]).commitId.substring(0, 8) + util.EOL)
    fs.appendFileSync(path, prefix + 'CI_COMMIT_REF_NAME' + suffix + '=' + branch + util.EOL)
    fs.appendFileSync(path, prefix + 'CI_COMMIT_TITLE' + suffix + '=' + lastCommitMsg + util.EOL)
    fs.appendFileSync(path, prefix + 'CI_SOURCE_NAME' + suffix + '=' + sourceName + util.EOL)
    fs.appendFileSync(path, prefix + 'CI_SOURCE_TYPE' + suffix + '=' + repoType + util.EOL)
    if(previousCommitId) {
        fs.appendFileSync(path, prefix + 'CI_COMMIT_BEFORE_SHA' + suffix + '=' + previousCommitId + util.EOL)
    }
    if(commitList.length > 0 && JSON.parse(commitList[0]).commitAuthor) {
        fs.appendFileSync(path, prefix + 'CI_COMMIT_AUTHOR' + suffix + '=' + decodeURIComponent(JSON.parse(commitList[0]).commitAuthor) + util.EOL)
    }

}

function writeCodeInfoToParams(
    prefix: string,
    suffix: string,
    commitList: string[],
    branch: string,
    lastCommitMsg: string,
    sourceName: string,
    repoType: string,
    previousCommitId?: string
) {
    if(previousCommitId) {
        step.addOutput(prefix + 'CI_COMMIT_BEFORE_SHA' + suffix + '=' + previousCommitId);
    }
    step.addOutput(prefix + 'CI_COMMIT_SHA' + suffix + '=' + JSON.parse(commitList[0]).commitId);
    step.addOutput(prefix + 'CI_COMMIT_ID' + suffix + '=' + JSON.parse(commitList[0]).commitId.substring(0, 8));
    step.addOutput(prefix + 'CI_COMMIT_REF_NAME' + suffix + '=' + branch);
    step.addOutput(prefix + 'CI_COMMIT_TITLE' + suffix + '=' + lastCommitMsg);
    step.addOutput(prefix + 'CI_SOURCE_NAME' + suffix + '=' + sourceName);
    step.addOutput(prefix + 'CI_SOURCE_TYPE' + suffix + '=' + repoType);
    if(commitList.length > 0 && JSON.parse(commitList[0]).commitAuthor) {
        step.addOutput(prefix + 'CI_COMMIT_AUTHOR' + suffix + '=' + decodeURIComponent(JSON.parse(commitList[0]).commitAuthor));
    }

}

export function buildCommit(line: string): string {
    let msgKey = '###commitMsg###:'
    let timeKey = '###commitTime###:'
    let authorKey = '###commitAuthor###:'
    let idKey = '###commitId###:'
    let indexMsg = line.indexOf(msgKey)
    let indexTime = line.indexOf(timeKey)
    let indexAuthor = line.indexOf(authorKey)
    let indexId = line.indexOf(idKey)
    let commitId = line.substring(indexId + idKey.length, indexTime)
    let commitTime = line.substring(indexTime + timeKey.length, indexAuthor)
    let commitAuthor = encodeURIComponent(line.substring(indexAuthor + authorKey.length, indexMsg))
    let commitMsg = encodeURIComponent(line.substring(indexMsg + msgKey.length, line.length))
    return `{"commitId": "${commitId}", "commitMsg": "${commitMsg}", "commitTime": "${commitTime}", "commitAuthor": "${commitAuthor}"}`
}

export function extractRepoName(gitRepoUrl: string): string {
    // 使用正则表达式匹配并提取仓库名称
    const match = gitRepoUrl.match(/([^\/]+)\/([^\/\.]+)(\.git)?$/);
    if (match) {
        return match[2];
    }
    return "";
}
